<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DeepSeek API 测试</title>
    <style>
        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            max-width: 800px;
            margin: 40px auto;
            padding: 20px;
            background-color: #f5f5f5;
        }

        .container {
            display: flex;
            flex-direction: column;
            gap: 20px;
        }

        h1 {
            color: #333;
            text-align: center;
        }

        textarea {
            width: 100%;
            height: 100px;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 16px;
            resize: vertical;
        }

        .button-group {
            display: flex;
            gap: 10px;
        }

        button {
            padding: 10px 20px;
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.2s;
        }

        button:hover {
            background-color: #0056b3;
        }

        button:disabled {
            background-color: #6c757d;
            cursor: not-allowed;
        }

        #stopButton {
            background-color: #dc3545;
        }

        #stopButton:hover:not(:disabled) {
            background-color: #c82333;
        }

        .response {
            padding: 15px;
            background-color: #fff;
            border: 1px solid #dee2e6;
            border-radius: 4px;
            white-space: pre-wrap;
            min-height: 100px;
            line-height: 1.6;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
        }

        .mode-toggle {
            margin-bottom: 10px;
            font-size: 16px;
        }

        .mode-toggle label {
            cursor: pointer;
        }
    </style>
</head>
<body>
<h1>DeepSeek API 测试</h1>
<div class="container">
    <div class="mode-toggle">
        <label>
            <input type="checkbox" id="streamMode" checked> 使用流式响应
        </label>
    </div>
    <div>
        <label for="question">问题：</label>
        <textarea id="question" placeholder="请输入您的问题...">你好，请介绍一下你自己。</textarea>
    </div>
    <div class="button-group">
        <button id="sendButton" onclick="sendQuestion()">发送问题</button>
        <button id="stopButton" onclick="stopResponse()" disabled>停止响应</button>
    </div>
    <div>
        <label for="response">回答：</label>
        <div id="response" class="response">等待回答...</div>
    </div>
</div>
<script>
    let abortController = null;
    let isStreaming = false;

    // 根据模式获取API地址
    function getApiUrl() {
        const isStream = document.getElementById('streamMode').checked;
        // !! 重要: 请确保这些URL与您的后端Spring Boot控制器路径匹配 !!
        return isStream
            ? 'http://localhost:8080/api/deepseek/chat-stream'
            : 'http://localhost:8080/api/deepseek/chat';
    }

    async function sendQuestion() {
        const question = document.getElementById('question').value;
        const responseDiv = document.getElementById('response');
        const sendButton = document.getElementById('sendButton');
        const stopButton = document.getElementById('stopButton');
        const streamMode = document.getElementById('streamMode').checked;
        // 重置状态
        responseDiv.textContent = '';
        sendButton.disabled = true;
        stopButton.disabled = false;
        isStreaming = true;
        // 创建新的 AbortController
        abortController = new AbortController();
        try {
            const apiUrl = getApiUrl();
            responseDiv.textContent = '正在思考...';
            const response = await fetch(apiUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    // 对于流式请求，可以明确告诉客户端我们期望接收SSE
                    'Accept': streamMode ? 'text/event-stream' : 'application/json'
                },
                body: JSON.stringify({question: question}),
                signal: abortController.signal
            });
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            if (streamMode) {
                await handleStreamResponse(response, responseDiv);
            } else {
                await handleNormalResponse(response, responseDiv);
            }
        } catch (error) {
            if (error.name === 'AbortError') {
                responseDiv.textContent += '\n\n[响应已停止]';
            } else {
                console.error("Fetch Error:", error);
                responseDiv.textContent = `错误: ${error.message}`;
            }
        } finally {
            sendButton.disabled = false;
            stopButton.disabled = true;
            isStreaming = false;
            abortController = null;
        }
    }

    // 处理流式响应
    async function handleStreamResponse(response, responseDiv) {
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';
        // 新增一个专门用于拼接JSON的缓冲区
        let jsonBuffer = '';
        // 清空初始提示
        responseDiv.textContent = '';
        while (isStreaming) {
            const {done, value} = await reader.read();
            if (done) break;
            buffer += decoder.decode(value, {stream: true});
            const lines = buffer.split('\n');
            buffer = lines.pop() || ''; // 保留最后一个不完整的行
            for (const line of lines) {
                if (line.startsWith('data:')) {
                    const data = line.substring(5).trim();
                    if (data === '[DONE]') {
                        isStreaming = false;
                        break;
                    }
                    // 忽略空的数据行
                    if (!data) {
                        continue;
                    }
                    // 将新的数据片段追加到JSON缓冲区
                    jsonBuffer += data;
                    try {
                        // 尝试解析累积的JSON字符串
                        const jsonData = JSON.parse(jsonBuffer);
                        // 如果解析成功，说明这是一个完整的JSON对象
                        const content = jsonData?.result?.output?.content;
                        if (content) {
                            responseDiv.textContent += content;
                        }
                        // 处理完毕后，清空JSON缓冲区，准备接收下一个JSON对象
                        jsonBuffer = '';
                    } catch (e) {
                        // 如果解析失败，说明JSON还不完整，什么也不做。
                        // 继续等待下一个数据片段来补全它。
                        // 这里不需要打印错误，这是正常的流处理行为。
                    }
                }
            }
        }
    }

    // 处理非流式响应
    async function handleNormalResponse(response, responseDiv) {
        const answer = await response.json();
        // !! 假设非流式接口返回格式为 {"generation": "..."} !!
        // 请根据您的实际后端返回格式修改这里的字段名
        const content = answer?.generation || '未收到有效回答';
        responseDiv.textContent = content;
    }

    function stopResponse() {
        if (abortController) {
            abortController.abort();
            isStreaming = false;
        }
    }

    // 监听 Enter 键发送
    document.getElementById('question').addEventListener('keydown', function (event) {
        if (event.key === 'Enter' && !event.shiftKey) {
            event.preventDefault();
            if (!document.getElementById('sendButton').disabled) {
                sendQuestion();
            }
        }
    });
</script>
</body>
</html>